﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Av.Utils;
using System.Threading;

namespace SymcmsPackage
{

    class SymcmsCreatorBase
    {

        protected string _inputDir = "";
        protected string _outputDir = "";
        protected string _symfonyInstallDir = "";

        /// <summary>
        /// Gets/sets directory to symcsm to copy from
        /// </summary>
        public string InputDir
        {
            get { return _inputDir; }
            set
            {
                // Ensure directory ends with backslash
                _inputDir = value.EndsWith("\\") ? value : string.Format("{0}\\", value);
            }
        }

        /// <summary>
        /// Gets/sets directory to copy to
        /// </summary>
        public string OutputDir
        {
            get { return _outputDir; }
            set
            {
                // Ensure directory ends with backslash
                _outputDir = value.EndsWith("\\") ? value : string.Format("{0}\\", value);
            }
        }

        /// <summary>
        /// Gets/sets directory to copy to
        /// </summary>
        public string SymfonyInstallDir
        {
            get { return _symfonyInstallDir; }
            set
            {
                // Ensure directory ends with backslash
                _symfonyInstallDir = value.EndsWith("\\") ? value : string.Format("{0}\\", value);
            }
        }

        protected class SymDirFiles
        {
            /// <summary>
            /// Gets/sets directory relative path from symfony sf10, i.e. "apps\\frontend\\config\\"
            /// </summary>
            public string Directory { get; private set; }

            /// <summary>
            /// Gets/sets files to be copied in this directory, only names, i.e. "config.php"
            /// </summary>
            public string[] Files { get; set; }

            public SymDirFiles(string dir, string[] files)
            {
                Directory = dir.EndsWith("\\") ? dir : dir + "\\";
                Files = files;
            }
        }

        virtual public void Create(/*string inputDir, string outputDir*/)
        {
            EnsureDirectories(/*InputDir, OutputDir, SymfonyInstallDir*/);

            SymDirFiles[] dirFiles = new SymDirFiles[] {
            new SymDirFiles("sf10\\apps\\frontend\\config\\"
                , new string[]
                {
                    "app.yml"
                    , "cache.yml"
                    , "config.php"
                    , "factories.yml"
                    , "filters.yml"
                    , "i18n.yml"
                    , "logging.yml"
                    , "module.yml"
                    , "routing.yml"
                    , "security.yml"
                    , "settings.yml"
                    , "view.yml"

                }
            )
            , new SymDirFiles("sf10\\config\\", new string[]
                {
                    "config.php"
                    , "databases.yml"
                }
            )
            , new SymDirFiles("sf10\\apps\\frontend\\templates\\", new string[]
                {
                    "empty.php"
                    , "layout.php"
                }
            )
            , new SymDirFiles("sf10\\apps\\frontend\\i18n\\", new string[]
                {
                    "messages.lt.xml"
                }
            )
            , new SymDirFiles("sf10\\apps\\frontend\\lib\\", new string[]
                {
                    "myUser.class.php"
                }
            )
            , new SymDirFiles("public_html\\", new string[]
                {
                    "functions.php"
                    , ".htaccess"
                    , "index.php"
                    , "test.php"
                }
            )
        };

            CopySymfonyFiles(dirFiles, InputDir, OutputDir);

        }

        protected void EnsureDirectories(/*string inputDir, string outputDir, string symfonyInstallDir*/)
        {
            if( !Directory.Exists(InputDir) )
            {
                throw new DirectoryNotFoundException("Directory to copy from [" + InputDir + "] not found!");
            }

            // Check that output directory exists and does not hava sf10 or public_html
            if(Directory.Exists(OutputDir))
            {
                if( Directory.Exists(OutputDir + "sf10") || Directory.Exists(OutputDir + "public_html") )
                {
                    throw new Exception("Directory to output symcsm project already contains sf10 or public_html directories [" + OutputDir + "!");
                }
            } else
            {
                Log4cs.Log("Creating directory to output symcms project {0}...", OutputDir);
                Directory.CreateDirectory(OutputDir);
            }

            // Here we are sure that output directory exists
            CreateSymfonyStructure(OutputDir);

            //string symfonyInstallDir = "D:\\install\\sf10\\";

            // Check if symfony install director is not just backslash
            if(SymfonyInstallDir.Length > 2)
            {
                Log4cs.Log("Going to copy symfony core files from {0}", SymfonyInstallDir);
                CopySymfonyCore();
            } else
            {
                Log4cs.Log(Importance.Warning, "Symfony install directory is not set, skip it!");
            }
        }

        private void CopySymfonyCore(/*object obj*/)
        {
            Log4cs.Log("Starting symfony copying thread...");
            //SymcmsPackageSettings settings = (SymcmsPackageSettings)obj;
            Log4cs.Log("Copy: " + SymfonyInstallDir + "data\\symfony\\" + " to: " + OutputDir + "sf10\\data\\symfony\\");
            Common.CopyDirectory(SymfonyInstallDir + "data\\symfony\\", OutputDir + "sf10\\data\\symfony\\");

            Log4cs.Log("Copy: " + SymfonyInstallDir + "lib\\symfony\\" + " to: " + OutputDir + "sf10\\lib\\symfony\\");
            Common.CopyDirectory(SymfonyInstallDir + "lib\\symfony\\", OutputDir + "sf10\\lib\\symfony\\");
        }

        protected void CreateSymfonyStructure(string symfonyDir)
        {
            string[] arDirs = new string[]{
                "sf10\\apps"
                , "sf10\\apps\\frontend\\"
                , "sf10\\apps\\frontend\\config\\"
                , "sf10\\apps\\frontend\\i18n\\"
                , "sf10\\apps\\frontend\\lib\\"
                , "sf10\\apps\\frontend\\modules\\"
                , "sf10\\apps\\frontend\\templates\\"
                , "sf10\\cache"
                , "sf10\\config"
                , "sf10\\lib\\"
                , "sf10\\lib\\helper\\"
                , "sf10\\lib\\model\\"
                , "sf10\\log"
                , "sf10\\plugins"
                , "public_html"
            };

            foreach(string dir in arDirs)
            {
                Directory.CreateDirectory(symfonyDir + dir);
            }
        }

        protected void CopySymfonyFiles(SymDirFiles[] dirFiles, string inputDir, string outputDir)
        {
            string fromDir = "";
            string toDir = "";
            foreach(SymDirFiles df in dirFiles)
            {
                fromDir = inputDir + df.Directory;
                toDir = outputDir + df.Directory;
                Log4cs.Log("Copy from {0} to {1}", fromDir, toDir);

                // Check if destination directory exists
                if( !Directory.Exists(toDir) )
                {
                    Log4cs.Log("Creating directory {0}...", toDir);
                    Directory.CreateDirectory(toDir);
                }

                // Copy only existing files
                foreach(string file in df.Files)
                {
                    if( File.Exists(fromDir + file) )
                    {
                        File.Copy(fromDir + file, toDir + file);
                    }
                }
            }
        }

        /// <summary>
        /// Copies modules to specified directory
        /// </summary>
        /// <param name="modules"></param>
        /// <param name="inputDir"></param>
        /// <param name="outputDir"></param>
        protected void CopySymfonyModules(string[] modules, string inputDir, string outputDir)
        {
            for(int i = 0; i < modules.Length; i++)
            {
                CopyModule(modules[i], inputDir, outputDir);
            }

            CreateSqlFile(m_slqFiles, outputDir + "!complete.sql");
            Log4cs.Log("Saving clean SQL file as: {0}...", outputDir + "!complete_clean.sql");
            File.WriteAllText(outputDir + "!complete_clean.sql", GetCleanSql(outputDir + "!complete.sql"));
        }

        /// <summary>
        /// Joins all SQL files from list to one and saves it in specified directory
        /// </summary>
        /// <param name="slqFiles"></param>
        /// <param name="outputDir"></param>
        public void CreateSqlFile(List<string> slqFiles, string outputFile)
        {
            StringBuilder sb = new StringBuilder();
            foreach(string file in slqFiles)
            {
                //lines = File.ReadAllLines(file);

                sb.AppendLine("------------------------------------------------------------");
                sb.AppendFormat("-- {0}", file).AppendLine();
                sb.AppendLine("------------------------------------------------------------");
                sb.Append(File.ReadAllText(file)).AppendLine();
                //Log4cs.Log("SQL: {0}", file);
            }

            Log4cs.Log("Saving complete SQL file as {0}...", outputFile);
            File.WriteAllText(outputFile, sb.ToString());
        }

        /// <summary>
        /// Returns clean "create table" SQL
        /// </summary>
        /// <param name="sqlFile">SQL file to read and clean</param>
        public string GetCleanSql(string sqlFile)
        {
            StringBuilder sb = new StringBuilder();
            string[] lines = File.ReadAllLines(sqlFile);
            bool wasRemark = false;

            foreach(string lin in lines)
            {
                if(lin.StartsWith("-") || lin.StartsWith("#") || lin.StartsWith("SET"))
                {
                    // Add 1 new line instead of several remarks
                    if( !wasRemark )
                    {
                        sb.AppendLine();
                    }
                    wasRemark = true;
                } else
                {
                    sb.AppendLine(lin);
                    wasRemark = false;
                }
            }

            return sb.ToString();
        }

        protected List<string> m_slqFiles = new List<string>();
        private void CopyModule(string module, string inputDir, string outputDir)
        {
            List<SymDirFiles> files = new List<SymDirFiles>();
            string relativeModuleDir = string.Format("sf10\\apps\\frontend\\modules\\{0}\\", module);

            // To list files in module subdirectories
            SymDirFiles df = null;
            string[] arFiles = null;


            if(!Directory.Exists(inputDir + relativeModuleDir))
            {
                Log4cs.Log(Importance.Error, "Module {0} does not exist in {1}!", module, inputDir);
                return;
            }

            // Check if we have SQL file for this module
            if( Directory.Exists(inputDir + relativeModuleDir + "doc") )
            {
                Log4cs.Log(Importance.Debug, "Searching for SQL files in {0}", inputDir + relativeModuleDir + "doc");
                string[] sqls = Directory.GetFiles(inputDir + relativeModuleDir + "doc", "*.sql");
                if( (sqls != null) && (sqls.Length > 0) )
                {
                    m_slqFiles.AddRange(sqls);
                }
            }

            // Copy actions
            files.Add(new SymDirFiles(relativeModuleDir + "actions", new string[]{
                                            "actions.class.php"
                                            , "components.class.php"
                                        }));

            // Copy only .yml files from "config" subdirectory
            df = new SymDirFiles(relativeModuleDir + "config\\", null);
            if(Directory.Exists(inputDir + df.Directory))
            {
                arFiles = Directory.GetFiles(inputDir + df.Directory, "*.yml");
                df.Files = new string[arFiles.Length];
                for(int i = 0; i < arFiles.Length; i++)
                {
                    df.Files[i] = arFiles[i].Substring(inputDir.Length + df.Directory.Length);
                }

                files.Add(df);
            }

            // Copy only .xml files from "i18n" subdirectory
            df = new SymDirFiles(relativeModuleDir + "i18n\\", null);
            if(Directory.Exists(inputDir + df.Directory))
            {
                arFiles = Directory.GetFiles(inputDir + df.Directory, "*.xml");
                df.Files = new string[arFiles.Length];
                for(int i = 0; i < arFiles.Length; i++)
                {
                    df.Files[i] = arFiles[i].Substring(inputDir.Length + df.Directory.Length);
                }

                files.Add(df);
            }

            // Copy only .xml files from "templates" subdirectory
            df = new SymDirFiles(relativeModuleDir + "templates\\", null);
            if(Directory.Exists(inputDir + df.Directory))
            {
                arFiles = Directory.GetFiles(inputDir + df.Directory, "*.php");
                df.Files = new string[arFiles.Length];
                for(int i = 0; i < arFiles.Length; i++)
                {
                    df.Files[i] = arFiles[i].Substring(inputDir.Length + df.Directory.Length);
                }

                files.Add(df);
            }

            // Copy only .xml files from "validate" subdirectory
            df = new SymDirFiles(relativeModuleDir + "validate\\", null);
            if(Directory.Exists(inputDir + df.Directory))
            {
                arFiles = Directory.GetFiles(inputDir + df.Directory, "*.yml");
                df.Files = new string[arFiles.Length];
                for(int i = 0; i < arFiles.Length; i++)
                {
                    df.Files[i] = arFiles[i].Substring(inputDir.Length + df.Directory.Length);
                }

                files.Add(df);
            }

            //foreach(var item in files)
            //{
            //    Log4cs.Log(item.Directory);
            //    foreach(var f in item.Files)
            //    {
            //        Log4cs.Log("  {0}", f);
            //    }
            //}
            CopySymfonyFiles(files.ToArray(), inputDir, outputDir);
        }



        /// <summary>
        /// Returns array of SQL files for symcms project
        /// </summary>
        /// <param name="inputDir">Should be absolute path to "sf10" directory</param>
        /// <returns></returns>
        public string[] GetSqlFilesList(string inputDir)
        {
            List<string> files = new List<string>();
            Stack<string> dirs = new Stack<string>();
            inputDir = inputDir.EndsWith("\\") ? inputDir : inputDir + "\\";

            // Prepare directories to scan for SQL files
            if( Directory.Exists(inputDir + "doc\\") )
            {
                Log4cs.Log("Going to scan: {0}", inputDir + "doc\\");
                dirs.Push(inputDir + "doc\\");
            }
            if( Directory.Exists(inputDir + "sf10\\apps\\frontend\\") )
            {
                Log4cs.Log("Going to scan: {0}", inputDir + "sf10\\apps\\frontend\\");
                dirs.Push(inputDir + "sf10\\apps\\frontend\\");
            }

            string[] subdirs = null;
            string dir = "";
            while( dirs.Count > 0 )
            {
                dir = dirs.Pop();

                // Add subdirectories to scan in next iterations
                subdirs = Directory.GetDirectories(dir);
                for(int i = 0; i < subdirs.Length; i++)
                {
                    dirs.Push(subdirs[i]);
                }

                files.AddRange(Directory.GetFiles(dir, "*.sql"));
            }

            return files.ToArray();
        }
    }

    class SymcmsCreator
    {

        public static SymcmsCreatorBase GetCreator(string projectType)
        {
            switch(projectType.ToLower())
            {
                case "mini":
                    return new SymcmsMiniCreator();
            }

            throw new ArgumentOutOfRangeException("Could not create symcms project of type [" + projectType + "]!");
        }

    }

    class SymcmsMiniCreator : SymcmsCreatorBase
    {

        override public void Create(/*string inputDir, string outputDir*/)
        {
            base.Create(/*InputDir, OutputDir*/);

            string[] modules = new string[]{
                "banner"
                , "bstat"
                , "cart"
                , "crop"
                , "draft"
                , "files"
                , "forms"
                , "gallery"
                , "log"
                , "mailtask"
                , "news"
                , "photos"
                , "seo"
                , "system"
                , "textmodule"
                , "tree"
                , "user"
            };

            CopySymfonyModules(modules, InputDir, OutputDir);

            #region " Libs copying "
		    
            SymDirFiles[] libs = new SymDirFiles[] {
                new SymDirFiles("sf10\\lib\\", new string[]{
                    "crop.class.php"
                    , "exceptionsUid.php"
                    , "LogRotate.php"
                    , "myConstants.class.php"
                    , "MyHelper.php"
                    , "mySqlUtf8Db.php"
                    , "priority.class.php"
                    , "passwordgenerator.class.php"
                })
                , new SymDirFiles("sf10\\lib\\helper\\", new string[]{
                    "SeoHelper.php"
                })
                , new SymDirFiles("sf10\\lib\\model\\", new string[]{
                    "BannerItem.php"
                    , "BannerItemPeer.php"
                    , "BannerStat.php"
                    , "BannerStatPeer.php"
                    , "Countries.php"
                    , "CountriesPeer.php"
                    , "Draft.php"
                    , "DraftPeer.php"
                    , "Files.php"
                    , "FilesPeer.php"
                    , "FilesI18n.php"
                    , "FilesI18nPeer.php"
                    , "Gallery.php"
                    , "GalleryPeer.php"
                    , "GalleryI18n.php"
                    , "GalleryI18nPeer.php"
                    , "GeoName.php"
                    , "GeoNamePeer.php"
                    , "Log.php"
                    , "LogPeer.php"
                    , "MailTask.php"
                    , "MailTaskPeer.php"
                    , "News.php"
                    , "NewsPeer.php"
                    , "NewsI18n.php"
                    , "NewsI18nPeer.php"
                    , "Newsletter.php"
                    , "NewsletterPeer.php"
                    , "Receipt.php"
                    , "ReceiptItem.php"
                    , "ReceiptItemPeer.php"
                    , "ReceiptPeer.php"
                    , "Seo.php"
                    , "SeoPeer.php"
                    , "SymConfig.php"
                    , "SymConfigPeer.php"
                    , "TextModule.php"
                    , "TextModulePeer.php"
                    , "TextModuleI18n.php"
                    , "TextModuleI18nPeer.php"
                    , "Tree.php"
                    , "TreePeer.php"
                    , "TreeLng.php"
                    , "TreeLngPeer.php"
                    , "Uid.php"
                    , "UidPeer.php"
                    , "User.php"
                    , "UserPeer.php"
                    , "Video.php"
                    , "VideoPeer.php"
                    , "VideoI18n.php"
                    , "VideoI18nPeer.php"
                })
                , new SymDirFiles("sf10\\lib\\model\\map\\", new string[]{
                    "BannerItemMapBuilder.php"
                    , "BannerStatMapBuilder.php"
                    , "CountriesMapBuilder.php"
                    , "DraftMapBuilder.php"
                    , "FilesMapBuilder.php"
                    , "FilesI18nMapBuilder.php"
                    , "GalleryMapBuilder.php"
                    , "GalleryI18nMapBuilder.php"
                    , "GeoNameMapBuilder.php"
                    , "LogMapBuilder.php"
                    , "MailTaskMapBuilder.php"
                    , "NewsMapBuilder.php"
                    , "NewsI18nMapBuilder.php"
                    , "NewsletterMapBuilder.php"
                    , "ReceiptItemMapBuilder.php"
                    , "ReceiptMapBuilder.php"
                    , "SeoMapBuilder.php"
                    , "SymConfigMapBuilder.php"
                    , "TextModuleMapBuilder.php"
                    , "TextModuleI18nMapBuilder.php"
                    , "TreeMapBuilder.php"
                    , "TreeLngMapBuilder.php"
                    , "UidMapBuilder.php"
                    , "UserMapBuilder.php"
                    , "VideoMapBuilder.php"
                    , "VideoI18nMapBuilder.php"
                })
                , new SymDirFiles("sf10\\lib\\model\\om\\", new string[]{
                    "BaseBannerItem.php"
                    , "BaseBannerItemPeer.php"
                    , "BaseBannerStat.php"
                    , "BaseBannerStatPeer.php"
                    , "BaseCountries.php"
                    , "BaseCountriesPeer.php"
                    , "BaseDraft.php"
                    , "BaseDraftPeer.php"
                    , "BaseFiles.php"
                    , "BaseFilesPeer.php"
                    , "BaseFilesI18n.php"
                    , "BaseFilesI18nPeer.php"
                    , "BaseGallery.php"
                    , "BaseGalleryPeer.php"
                    , "BaseGalleryI18n.php"
                    , "BaseGalleryI18nPeer.php"
                    , "BaseGeoName.php"
                    , "BaseGeoNamePeer.php"
                    , "BaseLog.php"
                    , "BaseLogPeer.php"
                    , "BaseMailTask.php"
                    , "BaseMailTaskPeer.php"
                    , "BaseNews.php"
                    , "BaseNewsPeer.php"
                    , "BaseNewsI18n.php"
                    , "BaseNewsI18nPeer.php"
                    , "BaseNewsletter.php"
                    , "BaseNewsletterPeer.php"
                    , "BaseReceipt.php"
                    , "BaseReceiptItem.php"
                    , "BaseReceiptItemPeer.php"
                    , "BaseReceiptPeer.php"
                    , "BaseSeo.php"
                    , "BaseSeoPeer.php"
                    , "BaseSymConfig.php"
                    , "BaseSymConfigPeer.php"
                    , "BaseTextModule.php"
                    , "BaseTextModulePeer.php"
                    , "BaseTextModuleI18n.php"
                    , "BaseTextModuleI18nPeer.php"
                    , "BaseTree.php"
                    , "BaseTreePeer.php"
                    , "BaseTreeLng.php"
                    , "BaseTreeLngPeer.php"
                    , "BaseUid.php"
                    , "BaseUidPeer.php"
                    , "BaseUser.php"
                    , "BaseUserPeer.php"
                    , "BaseVideo.php"
                    , "BaseVideoPeer.php"
                    , "BaseVideoI18n.php"
                    , "BaseVideoI18nPeer.php"
                })
            };
            CopySymfonyFiles(libs, InputDir, OutputDir);
 
	    #endregion        
        
        }


    }

}
